//#########################################################
//#
//# Titre : 	Utilitaires CHAT LINUX Automne 13
//#			SIF-1015 - Systeme d'exploitation
//#			Universite du Quebec a� Trois-Rivieres
//#
//# Auteur : 	Francois Meunier
//#	Date :	Septembre 2013
//#
//# Langage : 	ANSI C on LINUX 
//#
//#######################################

#include "gestionListeChaineeCHAT.h"

//Pointeur vers l'écran
extern WINDOW* contentscr;

//Pointeur de tete de liste
extern struct noeud* head;

extern int trxServerFD;

extern int idMembre;

//#######################################
//#
//# Interpretation de la transaction et 
//# creation du thread approprié 
//#
void* executeTransCHAT(void* newClientFD)
{	
	//pointeur sur la liste de threads
	struct threadNode* headThread;
	struct threadNode* tailThread;

	int continueChat = TRUE;
	int clientFD = *((int*)newClientFD);
	free(newClientFD);

	Pthread_detach(pthread_self());

	char message[MESSAGE_SIZE];

	waddstr(contentscr, "Nouveau client connecté!\n");
	wrefresh(contentscr);

	do
	{
		struct Info_FIFO_Transaction infoTrx;
		int byteRead = read(clientFD, &infoTrx, sizeof(infoTrx));

		if(byteRead == sizeof(infoTrx))
		{	
			waddstr(contentscr, "Transaction : ");		
			waddstr(contentscr, infoTrx.transaction);
			waddstr(contentscr, "\n");
			wrefresh(contentscr);
		}

		char *tok, *sp;
		int isInvalid = FALSE;
		char originalTransaction[TRANSACTION_SIZE];
		strcpy(originalTransaction, infoTrx.transaction);

		//Extraction du type de transaction
		tok = strtok_r(infoTrx.transaction, " ", &sp);

		if(tok != NULL && strlen(tok) == 1 )//Valide qu'il n'y a qu'un seul caratere suivi d'un espace au debut de la transaction
		{
			//Branchement selon le type de transaction
			switch(toupper(tok[0]))
			{
				case 'A':{
					//Extraction des parametres
					char *ptrNick = strtok_r(NULL, " ", &sp);
					char *ptrGroupe = strtok_r(NULL, "\n", &sp);
		
					if(ptrNick != NULL && ptrGroupe != NULL)
					{
						int lenghtNick = strlen(ptrNick);
						int lenghtGroupe = strlen(ptrGroupe);
						if(lenghtNick > 0 && lenghtNick <= MAX_CHAT_STR_LEN
						&& lenghtGroupe> 0 && lenghtGroupe <= MAX_CHAT_STR_LEN)
						{
							//Creation du parametre
							struct infoMembre* membre = (struct infoMembre*)Malloc(sizeof(struct infoMembre));
		
							//Incrementation de l'identifiant
							idMembre++;
		
							//Assignation des valeurs du parametre
							membre->identifiant = idMembre;
							strcpy(membre->ptrNick, ptrNick);
							strcpy(membre->ptrGroupe, ptrGroupe);
							membre->pid =  infoTrx.pid_client;
							membre->fd =  clientFD;

							//Cree le nouveau noeud dans la liste de threads
							initThreadNode(&headThread, &tailThread);
		
							Pthread_create(&(tailThread->thread), NULL, &addItemCHAT, membre);
						}
						else
						{
							isInvalid = TRUE;
						}
					}
					else
					{
						isInvalid = TRUE;
					}
					break;
					}
				case 'M':{
					//Extraction des parametres
					char* idStr = strtok_r(NULL, " ", &sp);
					int identifiant = 0;

					if(idStr != NULL)
						identifiant = atoi(idStr);
					else
						isInvalid = TRUE;

					if(isInvalid == FALSE )	
					{
						char *ptrNick = strtok_r(NULL, " ", &sp);
						char *ptrGroupe = strtok_r(NULL, "\n", &sp);
		
						if(ptrNick != NULL && ptrGroupe != NULL)
						{
							int lenghtNick = strlen(ptrNick);
							int lenghtGroupe = strlen(ptrGroupe);
							if(lenghtNick > 0 && lenghtNick <= MAX_CHAT_STR_LEN
							&& lenghtGroupe > 0 && lenghtGroupe <= MAX_CHAT_STR_LEN
							&& identifiant != 0)//Identifiant == 0 si on fait atoi() sur une chaine qui n'est pas une chiffre 							    ou ne debute pas par un chiffre et les id utilisateurs commencent a 1
							{
								//Creation du parametre
								struct infoMembre* membre = (struct infoMembre*)Malloc(sizeof(struct infoMembre));			
								//Assignation des valeurs du parametre
								membre->identifiant = identifiant;
								membre->fd =  clientFD;
								strcpy(membre->ptrNick, ptrNick);
								strcpy(membre->ptrGroupe, ptrGroupe);

								//Pid du client demandant							
								membre->pid =  infoTrx.pid_client;

								//Cree le nouveau noeud dans la liste de threads
								initThreadNode(&headThread, &tailThread);

								Pthread_create(&(tailThread->thread),NULL,&modifyItemCHAT,membre);
							}
							else
							{
								isInvalid = TRUE;
							}
						}
						else
						{
							isInvalid = TRUE;
						}
					}
					break;
					}
				case 'E':{
					//Extraction du parametre
					char *ptrNick = strtok_r(NULL, " ", &sp);
					char *ptrGroupe = strtok_r(NULL, "\n", &sp);

					if(ptrNick != NULL && ptrGroupe != NULL)
					{
						int lenghtNick = strlen(ptrNick);
						int lenghtGroupe = strlen(ptrGroupe);
						if(lenghtNick > 0 && lenghtNick <= MAX_CHAT_STR_LEN
						&& lenghtGroupe > 0 && lenghtGroupe <= MAX_CHAT_STR_LEN)
						{
							//Creation du parametre
							struct suppressionMembre* membre  = (struct suppressionMembre*)Malloc(sizeof(struct suppressionMembre));
	
							//Assignation des valeurs du parametre
							strcpy(membre->ptrNick, ptrNick);
							strcpy(membre->ptrGroupe, ptrGroupe);
							membre->fd =  clientFD;
							//Pid du client demandant
							membre->pid =  infoTrx.pid_client;
		
							//Cree le nouveau noeud dans la liste de threads
							initThreadNode(&headThread, &tailThread);
		
							Pthread_create(&(tailThread->thread), NULL, &removeItemCHAT, membre);
						}
						else
						{
							isInvalid = TRUE;
						}
					}
					else
					{
						isInvalid = TRUE;
					}
					break;
					}
				case 'L':{
					//Extraction des parametres
					char* strStart = strtok_r(NULL, "-", &sp);
					char* strEnd = strtok_r(NULL, " ", &sp);
			
					if(strStart != NULL && strEnd != NULL)
					{
						int nstart = atoi(strStart);
						int nend = atoi(strEnd);
						if(nstart != 0 && nend != 0 && nstart <= nend)
						{
							//Creation du parametre
							struct paramsIntervalle* intervalle = (struct paramsIntervalle*)Malloc(sizeof(struct paramsIntervalle));
							intervalle->debut = nstart;
							intervalle->fin = nend;
							intervalle->pid =  infoTrx.pid_client;
							intervalle->fd =  clientFD;

							//Cree le nouveau noeud dans la liste de threads
							initThreadNode(&headThread, &tailThread);
		
							Pthread_create(&(tailThread->thread), NULL, &listItemsCHAT, intervalle);
						}
						else
						{
							isInvalid = TRUE;
						}
					}
					else
					{
						isInvalid = TRUE;
					}
					break;
					}
				case 'T':{
					//Extraction des parametres
					char *ptrNick = strtok_r(NULL, " ", &sp);
					char *ptrGroupe = strtok_r(NULL, " ", &sp);
					char *ptrTexte = strtok_r(NULL, "\n", &sp);

					if(ptrNick != NULL && ptrGroupe != NULL  && ptrTexte != NULL)
					{
						int lenghtNick = strlen(ptrNick);
						int lenghtGroupe = strlen(ptrGroupe);
						int lenghtTexte = strlen(ptrTexte);
						if(lenghtNick > 0 && lenghtNick <= MAX_CHAT_STR_LEN
						&& lenghtGroupe > 0 && lenghtGroupe <= MAX_CHAT_STR_LEN
						&& lenghtTexte > 0 && lenghtTexte <= MAX_CHAT_STR_LEN)
						{
							//Creation du parametre
							struct paramsTexte* message = (struct paramsTexte*)Malloc(sizeof(struct paramsTexte));
							strcpy(message->ptrNick, ptrNick);
							strcpy(message->ptrGroupe, ptrGroupe);
							strcpy(message->ptrTexte, ptrTexte);
							message->pid =  infoTrx.pid_client;
		
							//Cree le nouveau noeud dans la liste de threads
							initThreadNode(&headThread, &tailThread);
		
							Pthread_create(&(tailThread->thread), NULL, &transTextCHAT, message);
						}
						else
						{
							isInvalid = TRUE;
						}
					}
					else
					{
						isInvalid = TRUE;
					}
					break;
					}
				case 'Q':{
					//Extraction des parametres
					char* strPid = strtok_r(NULL, ".", &sp);

					if(strPid != NULL)
					{
						unsigned int pid = atoi(strPid);

						if(pid > 0)
						{
							//Creation du parametre
							struct paramQuit* quitPid = (struct paramQuit*)Malloc(sizeof(struct paramQuit));
							quitPid->pid =  infoTrx.pid_client;
		
							//Cree le nouveau noeud dans la liste de threads
							initThreadNode(&headThread, &tailThread);
		
							Pthread_create(&(tailThread->thread), NULL, &quitUser, quitPid);
							continueChat = FALSE;
						}
						else
						{
							isInvalid = TRUE;
						}
					}
					else
					{
						isInvalid = TRUE;
					}
					break;
					}
				default: isInvalid = TRUE;
					break;
			}
		}
		else
		{
			isInvalid = TRUE;
		}

		if(isInvalid == TRUE)
		{
			memset(message, '\0', MESSAGE_SIZE);
			sprintf(message, "Transaction invalide : %s\n", originalTransaction);

			wattron(contentscr, COLOR_PAIR(RED_ON_BLACK));
			waddstr(contentscr, message);
			wrefresh(contentscr);
			wattron(contentscr, COLOR_PAIR(GREEN_ON_BLACK));

			memset(message, '\0', sizeof(message));
			sprintf(message, "\nTransaction invalide!\n");
			sendMessage(clientFD, 'F', message);
		}
	}
	while(continueChat == TRUE);

	//Attente de la terminaison des threads et liberation de la mémoire de chaque noeud
	struct threadNode* ittThread = headThread;
	struct threadNode* tempThread = NULL;
	while (ittThread != NULL)
	{
		Pthread_join(ittThread->thread, NULL);

		tempThread = ittThread->suivant;

		free(ittThread);

		ittThread = tempThread;
	}

	sendMessage(clientFD,'R',"Deconnexion");

	close(clientFD);	

	pthread_exit(0);
}

//#######################################
//#
//# Initialisation d'un noeud de la liste de threads
//#
void initThreadNode(struct threadNode** head, struct threadNode** tail)
{

	struct threadNode* newThreadNode = (struct threadNode*)Malloc(sizeof(struct threadNode));	
	newThreadNode->suivant = NULL;

	if(*head == NULL)
	{
		*head = newThreadNode;
		*tail = *head;
	}
	else 
	{
		(*tail)->suivant = newThreadNode;
		*tail = (*tail)->suivant;
	}
}
